import pgzrun
import random
from 已保存关卡 import ALL_LEVELS


# =============================================================================
# 游戏配置常量
# =============================================================================
PLAYER_SPEED = 4.5
PLAYER_JUMP = -12
GRAVITY = 0.7
ENEMY_DEFAULT_SPEED = 1.5

TILE_SIZE = 40
WIDTH = 800
HEIGHT = 600

# 预计算常量（减少重复计算）
VIEWPORT_MARGIN = WIDTH * 1.5      # 统一视口边距
CAMERA_OFFSET_X = WIDTH / 3
CAMERA_SMOOTH = 0.1
HUD_SCORE_POS = (10, 10)
HUD_LIVES_POS = (WIDTH - 150, 10)

# 输入系统优化配置（移动端性能优化）
INPUT_UPDATE_INTERVAL = 2          # 输入更新间隔（帧）：1=每帧更新，2=每2帧更新，3=每3帧更新
                                   # 建议值：移动端2-3，PC端1
INPUT_POSITION_THRESHOLD = 10      # 位置变化阈值（像素）：手指移动超过此距离才重新检测按钮
                                   # 建议值：移动端8-12，精确控制5-8

# 视口更新优化配置（移动端卡顿优化）
VIEWPORT_UPDATE_THRESHOLD = 100    # 视口更新阈值（像素）：玩家移动超过此距离才重新计算可见对象
                                   # 建议值：移动端100-200，精确游戏50-100
                                   # 值越大=更新越少=性能越好，但可能出现对象突然出现


# =============================================================================
# 游戏状态类（封装状态管理，减少全局变量）
# =============================================================================
class GameState:
    """游戏状态管理类 - 统一管理所有游戏状态和缓存"""
    
    __slots__ = ('objects', 'next_id', 'player_id', 'goal_id', 
                 'level_index', 'level_width', 'state',
                 'score', 'score_display', 'lives', 'coins_for_life',
                 'camera_x', 'plats', 'enemies', 'interactables', 'cache_dirty',
                 'score_text', 'lives_text', 'last_score', 'last_lives',
                 'frame_count',  # 帧计数器（用于输入更新频率控制）
                 'visible_plats', 'visible_interactables',  # 每帧可见对象缓存
                 'last_viewport_x')  # 上次视口中心位置（用于增量更新）
    
    def __init__(self):
        self.objects = {}
        self.next_id = 0
        self.player_id = -1
        self.goal_id = -1
        
        self.level_index = 0
        self.level_width = 0
        self.state = 'playing'
        
        self.score = 0
        self.score_display = 0
        self.lives = 1
        self.coins_for_life = 0
        
        self.camera_x = 0
        
        # 统一的缓存管理
        self.plats = []
        self.enemies = []
        self.interactables = []
        self.cache_dirty = True
        
        # HUD缓存
        self.score_text = "分数: 0"
        self.lives_text = "生命: 1"
        self.last_score = 0
        self.last_lives = 1
        
        # 帧计数器
        self.frame_count = 0
        
        # 每帧可见对象缓存（减少重复计算）
        self.visible_plats = []
        self.visible_interactables = []
        self.last_viewport_x = -999999  # 初始化为一个很远的值，确保首次更新
    
    def get_player(self):
        """获取玩家对象"""
        return self.objects.get(self.player_id)
    
    def create_object(self, props):
        """创建游戏对象"""
        obj_id = self.next_id
        props['id'] = obj_id
        props['actor'] = Actor(props.get('image_name', 'platform'))
        self.objects[obj_id] = props
        self.next_id += 1
        self.cache_dirty = True
        return obj_id
    
    def destroy_object(self, obj_id):
        """销毁对象"""
        if obj_id in self.objects:
            del self.objects[obj_id]
            self.cache_dirty = True
    
    def update_caches(self):
        """更新缓存"""
        if not self.cache_dirty:
            return
        
        self.plats.clear()
        self.enemies.clear()
        self.interactables.clear()
        
        for obj in self.objects.values():
            t = obj['type']
            if t in ('platform', 'question_block', 'spring'):
                self.plats.append(obj)
            elif t == 'enemy':
                self.enemies.append(obj)
                self.interactables.append(obj)
            elif t in ('coin', 'goal'):
                self.interactables.append(obj)
        
        self.cache_dirty = False
    
    def get_visible(self, player_x, obj_list):
        """获取视口内的对象"""
        left = player_x - VIEWPORT_MARGIN
        right = player_x + VIEWPORT_MARGIN
        return [o for o in obj_list if o['x'] + o['width'] >= left and o['x'] <= right]
    
    def update_visible_cache(self, player_x):
        """更新可见对象缓存（增量更新优化 - 只在玩家移动显著时才更新）"""
        # 优化：只在玩家移动超过阈值时才重新计算
        # 这样可以避免每帧都重新创建列表和遍历所有对象
        if abs(player_x - self.last_viewport_x) < VIEWPORT_UPDATE_THRESHOLD:
            return  # 移动距离不够，使用缓存，不更新
        
        # 更新视口中心位置
        self.last_viewport_x = player_x
        
        # 预计算边界（避免在循环中重复计算）
        left = player_x - VIEWPORT_MARGIN
        right = player_x + VIEWPORT_MARGIN
        
        # 清空并重用列表（避免创建新列表）
        self.visible_plats.clear()
        self.visible_interactables.clear()
        
        # 简化的过滤逻辑（用for循环替代列表推导，更直观更快）
        for p in self.plats:
            # 简化的边界检测：只检查对象右边界和左边界
            if p['x'] <= right and p['x'] + p['width'] >= left:
                self.visible_plats.append(p)
        
        for o in self.interactables:
            if o['x'] <= right and o['x'] + o['width'] >= left:
                self.visible_interactables.append(o)


# =============================================================================
# 输入系统类（封装输入管理）
# =============================================================================
class Input:
    """输入管理类 - 优化移动端触摸性能"""
    
    __slots__ = ('keys', 'mouse_pressed', 'mouse_pos', 'jump_last_frame',
                 'left', 'right', 'up', 
                 'cached_btn', 'last_check_pos')  # 缓存当前按钮和上次检测位置
    
    def __init__(self):
        self.keys = {'left': False, 'right': False, 'up': False}
        self.mouse_pressed = False
        self.mouse_pos = (0, 0)
        self.jump_last_frame = False
        
        self.left = False
        self.right = False
        self.up = False
        
        # 性能优化：缓存当前按下的按钮
        self.cached_btn = None  # 缓存当前激活的按钮名称
        self.last_check_pos = (-1, -1)  # 上次检测的鼠标位置
    
    def update(self, ui_buttons):
        """更新输入状态（优化版 - 减少移动端卡顿）"""
        ml = mr = mu = False
        
        if self.mouse_pressed:
            mx, my = self.mouse_pos
            
            # 优化1：只在鼠标位置显著变化时才重新检测按钮（减少微小抖动触发）
            # 使用可配置的位置阈值，避免手指轻微移动导致频繁重新检测
            last_x, last_y = self.last_check_pos
            pos_changed = abs(mx - last_x) > INPUT_POSITION_THRESHOLD or abs(my - last_y) > INPUT_POSITION_THRESHOLD
            
            if pos_changed:
                self.last_check_pos = (mx, my)
                self.cached_btn = None
                
                # 优化2：按使用频率顺序检测按钮（跳跃 > 右 > 左）
                # 先检测最常用的，可以更快找到匹配
                if ui_buttons['jump']['x1'] <= mx < ui_buttons['jump']['x2'] and \
                   ui_buttons['jump']['y1'] <= my < ui_buttons['jump']['y2']:
                    self.cached_btn = 'jump'
                elif ui_buttons['right']['x1'] <= mx < ui_buttons['right']['x2'] and \
                     ui_buttons['right']['y1'] <= my < ui_buttons['right']['y2']:
                    self.cached_btn = 'right'
                elif ui_buttons['left']['x1'] <= mx < ui_buttons['left']['x2'] and \
                     ui_buttons['left']['y1'] <= my < ui_buttons['left']['y2']:
                    self.cached_btn = 'left'
            
            # 优化3：使用缓存的按钮状态（避免每帧重复检测）
            if self.cached_btn == 'left':
                ml = True
            elif self.cached_btn == 'right':
                mr = True
            elif self.cached_btn == 'jump':
                mu = True
        else:
            # 鼠标未按下时清除缓存
            self.cached_btn = None
            self.last_check_pos = (-1, -1)
        
        self.left = self.keys['left'] or ml
        self.right = self.keys['right'] or mr
        self.up = self.keys['up'] or mu


# =============================================================================
# 全局实例
# =============================================================================
game = GameState()
input_sys = Input()

# UI按钮（预计算边界，避免每帧计算）
ui_buttons = {
    'left': {'actor': Actor('ui_left'), 'x1': 20, 'y1': HEIGHT - 100, 'x2': 100, 'y2': HEIGHT - 20},
    'right': {'actor': Actor('ui_right'), 'x1': 120, 'y1': HEIGHT - 100, 'x2': 200, 'y2': HEIGHT - 20},
    'jump': {'actor': Actor('ui_jump'), 'x1': WIDTH - 100, 'y1': HEIGHT - 100, 'x2': WIDTH - 20, 'y2': HEIGHT - 20}
}

for name, btn in ui_buttons.items():
    btn['actor'].pos = ((btn['x1'] + btn['x2']) / 2, (btn['y1'] + btn['y2']) / 2)

restart_button = Actor('restart_button', (WIDTH / 2, HEIGHT / 2 + 140))
RESTART_BOUNDS = (restart_button.left, restart_button.top, restart_button.width, restart_button.height)

pending_restart = False


# =============================================================================
# 工具函数
# =============================================================================
def collision(x1, y1, w1, h1, x2, y2, w2, h2):
    """优化的碰撞检测（内联减少函数调用）"""
    return x1 < x2 + w2 and x1 + w1 > x2 and y1 < y2 + h2 and y1 + h1 > y2


# =============================================================================
# 对象创建函数（简化版）
# =============================================================================
def create_platform(x, y, ptype, image):
    """创建平台类对象"""
    return game.create_object({
        'x': x, 'y': y, 'width': TILE_SIZE, 'height': TILE_SIZE,
        'type': ptype, 'image_name': image
    })


def create_enemy(x, y, vx=None):
    """创建敌人"""
    return game.create_object({
        'x': x, 'y': y, 'width': TILE_SIZE - 4, 'height': TILE_SIZE,
        'type': 'enemy', 'image_name': 'enemy_walk1',
        'vx': vx or random.choice([-1.5, 1.5]), 'vy': 0
    })


def create_coin(x, y, collectible=True, vy=None, timer=None):
    """创建金币"""
    props = {
        'x': x, 'y': y, 'width': TILE_SIZE, 'height': TILE_SIZE,
        'type': 'coin', 'image_name': 'coin', 'is_collectible': collectible
    }
    if vy is not None:
        props['vy'] = vy
    if timer is not None:
        props['spawn_timer'] = timer
    return game.create_object(props)


def create_player(x, y):
    """创建玩家"""
    return game.create_object({
        'x': x, 'y': y, 'width': TILE_SIZE - 10, 'height': TILE_SIZE,
        'type': 'player', 'image_name': 'player_idle',
        'vx': 0, 'vy': 0, 'on_ground': False,
        'start_pos': (x, y), 'last_safe_pos': (x, y)
    })


# =============================================================================
# 关卡管理
# =============================================================================
def setup_level(level_data):
    """设置关卡"""
    game.objects.clear()
    game.next_id = 0
    game.cache_dirty = True
    
    player_start = (100, 100)
    game.level_width = max(len(row) for row in level_data) * TILE_SIZE if level_data else 0
    
    for r, row in enumerate(level_data):
        for c, char in enumerate(row):
            x, y = c * TILE_SIZE, r * TILE_SIZE
            
            if char == 'X':
                create_platform(x, y, 'platform', 'brick')
            elif char == 'S':
                create_platform(x, y, 'spring', 'spring')
            elif char == 'F':
                game.goal_id = create_platform(x, y, 'goal', 'flag')
            elif char == 'P':
                player_start = (x, y)
            elif char == 'E':
                create_enemy(x, y, -ENEMY_DEFAULT_SPEED)
            elif char == 'C':
                create_coin(x, y)
            elif char == '?':
                create_platform(x, y, 'question_block', 'question_block')
    
    game.player_id = create_player(*player_start)


def start_game():
    """开始游戏"""
    game.level_index = 0
    game.state = 'playing'
    game.score = game.score_display = 0
    game.lives = 1
    game.coins_for_life = 0
    game.camera_x = 0
    game.score_text = "分数: 0"
    game.lives_text = "生命: 1"
    game.last_score = game.last_lives = 0
    game.frame_count = 0  # 重置帧计数器
    setup_level(ALL_LEVELS[0])


def next_level():
    """下一关"""
    game.level_index += 1
    if game.level_index >= len(ALL_LEVELS):
        game.state = 'victory'
        game.score += 1000
    else:
        setup_level(ALL_LEVELS[game.level_index])


def player_death():
    """玩家死亡"""
    game.lives -= 1
    if game.lives < 0:
        game.state = 'game_over'
    else:
        p = game.get_player()
        if p:
            pos = p.get('last_safe_pos', p['start_pos'])
            p['x'], p['y'] = pos
            p['vx'] = p['vy'] = 0
            p['on_ground'] = False


# =============================================================================
# 物理系统
# =============================================================================
def update_physics():
    """更新玩家物理"""
    p = game.get_player()
    if not p:
        return
    
    px, py, pw, ph = p['x'], p['y'], p['width'], p['height']
    
    # 使用预缓存的可见平台列表（避免每帧重复计算）
    plats = game.visible_plats
    
    # 水平移动
    p['vx'] = 0
    if input_sys.right:
        p['vx'] = PLAYER_SPEED
    elif input_sys.left:
        p['vx'] = -PLAYER_SPEED
    
    px += p['vx']
    if game.level_width > 0:
        px = max(0, min(px, game.level_width - pw))
    
    # 水平碰撞
    for plat in plats:
        if collision(px, py, pw, ph, plat['x'], plat['y'], plat['width'], plat['height']):
            px = plat['x'] - pw if p['vx'] > 0 else plat['x'] + plat['width']
            p['vx'] = 0
            break
    
    p['x'] = px
    
    # 垂直移动
    p['on_ground'] = False
    p['vy'] += GRAVITY
    py += p['vy']
    
    # 垂直碰撞
    for plat in plats:
        if collision(px, py, pw, ph, plat['x'], plat['y'], plat['width'], plat['height']):
            if p['vy'] > 0:  # 下落
                py = plat['y'] - ph
                p['vy'] = 0
                p['on_ground'] = True
                
                pt = plat['type']
                if pt in ('platform', 'question_block'):
                    p['last_safe_pos'] = (px, py)
                elif pt == 'spring':
                    p['vy'] = -22
                    p['on_ground'] = False
            
            elif p['vy'] < 0:  # 上升
                py = plat['y'] + plat['height']
                p['vy'] = 0
                
                if plat['type'] == 'question_block':
                    plat['type'] = 'platform'
                    plat['image_name'] = 'brick'
                    if random.randint(1, 2) == 1:
                        create_coin(plat['x'], plat['y'] - TILE_SIZE, False, -8, 20)
                    else:
                        create_enemy(plat['x'], plat['y'] - TILE_SIZE)
            break
    
    p['y'] = py
    
    # 跳跃
    if input_sys.up and not input_sys.jump_last_frame and p['on_ground']:
            p['vy'] = PLAYER_JUMP
            p['on_ground'] = False
    
    input_sys.jump_last_frame = input_sys.up


# =============================================================================
# 交互系统
# =============================================================================
def update_interactions():
    """更新交互"""
    p = game.get_player()
    if not p:
        return
    
    px, py, pw, ph = p['x'], p['y'], p['width'], p['height']
    pvy = p['vy']
    
    hx, hy, hw, hh = px + 5, py + 10, pw - 10, ph - 10
    to_destroy = []
    
    # 使用预缓存的可见交互对象列表（避免每帧重复计算）
    objs = game.visible_interactables
    
    for obj in objs:
        ox, oy, ow, oh = obj['x'], obj['y'], obj['width'], obj['height']
        
        # 快速距离检测
        if ox > px + pw + 20 or ox + ow < px - 20 or oy > py + ph + 20 or oy + oh < py - 20:
            continue
        
        oid = obj['id']
        
        if obj['type'] == 'enemy':
            if collision(hx, hy, hw, hh, ox, oy, ow, oh):
                if pvy > 0 and py + ph - pvy <= oy:
                    p['vy'] = -8
                    p['on_ground'] = False
                    game.score += 200
                    to_destroy.append(oid)
                else:
                    player_death()
                    return
        
        elif obj['type'] == 'coin' and obj.get('is_collectible'):
            if collision(px, py, pw, ph, ox, oy, ow, oh):
                game.score += 100
                to_destroy.append(oid)
                game.coins_for_life += 1
                if game.coins_for_life >= 3:
                    game.lives += 1
                    game.coins_for_life = 0
        
        elif oid == game.goal_id:
            if collision(px, py, pw, ph, ox, oy, ow, oh):
                next_level()
                return
    
    for oid in to_destroy:
        game.destroy_object(oid)
    
    if py > HEIGHT:
        player_death()


# =============================================================================
# AI系统
# =============================================================================
def update_ai():
    """更新敌人AI"""
    p = game.get_player()
    if not p:
        return
    
    px = p['x']
    
    # 使用预缓存的可见平台列表（避免每帧重复计算）
    plats = game.visible_plats
    left_bound = px - VIEWPORT_MARGIN
    right_bound = px + VIEWPORT_MARGIN
    
    for e in game.enemies:
        ex = e['x']
        if ex + e['width'] < left_bound or ex > right_bound:
            continue
            
        ey, ew, eh, evx = e['y'], e['width'], e['height'], e['vx']
        
        # 水平移动
        ex += evx
        for pl in plats:
            if collision(ex, ey, ew, eh, pl['x'], pl['y'], pl['width'], pl['height']):
                ex -= evx
                evx *= -1
                break
        
        e['x'], e['vx'] = ex, evx
        
        # 边缘检测（防止敌人掉下平台）
        check_x = ex - 1 if evx < 0 else ex + ew + 1
        check_y = ey + eh + 1
        if not any(collision(check_x, check_y, 1, 1, pl['x'], pl['y'], pl['width'], pl['height']) for pl in plats):
            e['vx'] *= -1
    
    # 金币动画
    for obj in game.interactables:
        if obj['type'] == 'coin' and 'spawn_timer' in obj:
            obj['vy'] += GRAVITY
            obj['y'] += obj['vy']
            obj['spawn_timer'] -= 1
            if obj['spawn_timer'] <= 0:
                obj['is_collectible'] = True
                del obj['spawn_timer'], obj['vy']


# =============================================================================
# 摄像机和HUD
# =============================================================================
def update_camera():
    """更新摄像机"""
    p = game.get_player()
    if p:
        target = p['x'] - CAMERA_OFFSET_X
        clamped = max(0, min(target, max(0, game.level_width - WIDTH)))
        game.camera_x += (clamped - game.camera_x) * CAMERA_SMOOTH


def update_hud():
    """更新HUD"""
    if abs(game.score - game.score_display) < 1:
        game.score_display = game.score
    else:
        game.score_display += (game.score - game.score_display) * 0.1
    
    score_int = int(game.score_display)
    if score_int != game.last_score:
        game.score_text = f"分数: {score_int}"
        game.last_score = score_int
    
    if game.lives != game.last_lives:
        game.lives_text = f"生命: {int(game.lives)}"
        game.last_lives = game.lives


def sync_actors():
    """同步Actor位置"""
    for obj in game.objects.values():
        a = obj['actor']
        a.x = obj['x'] + obj['width'] * 0.5
        a.y = obj['y'] + obj['height'] * 0.5
        if a.image != obj['image_name']:
            a.image = obj['image_name']


# =============================================================================
# 主循环
# =============================================================================
def update():
    """主更新函数"""
    global pending_restart
    
    if pending_restart:
        pending_restart = False
        start_game()
        return
    
    if game.state == 'playing':
        # 帧计数器递增
        game.frame_count += 1
        
        # 更新对象缓存
        game.update_caches()
        
        # 获取玩家位置并更新可见对象缓存（关键优化：统一计算一次）
        player = game.get_player()
        if player:
            game.update_visible_cache(player['x'])
        
        # 输入更新（支持降频优化）
        # INPUT_UPDATE_INTERVAL=1: 每帧更新（60 FPS输入）
        # INPUT_UPDATE_INTERVAL=2: 每2帧更新（30 FPS输入）
        # INPUT_UPDATE_INTERVAL=3: 每3帧更新（20 FPS输入）
        if game.frame_count % INPUT_UPDATE_INTERVAL == 0:
            input_sys.update(ui_buttons)
        
        # 其他系统正常每帧更新（使用预缓存的可见对象，无需重复计算）
        update_physics()
        update_interactions()
        update_ai()
        sync_actors()
        update_camera()
        update_hud()


def draw():
    """主绘制函数"""
    if game.state == 'playing':
        draw_playing()
    elif game.state == 'victory':
        draw_victory()
    elif game.state == 'game_over':
        draw_game_over()


def draw_playing():
    """绘制游戏画面"""
    screen.clear()
    screen.fill('lightskyblue')
    
    cam_right = game.camera_x + WIDTH
    
    for obj in game.objects.values():
        ox = obj['x']
        if ox + obj['width'] >= game.camera_x and ox <= cam_right:
            a = obj['actor']
            a.x = ox + obj['width'] * 0.5 - game.camera_x
            a.y = obj['y'] + obj['height'] * 0.5
            a.draw()
    
    for btn in ui_buttons.values():
        btn['actor'].draw()
    
    screen.draw.text(game.score_text, HUD_SCORE_POS, fontsize=40, color="white", owidth=1, ocolor="black")
    screen.draw.text(game.lives_text, HUD_LIVES_POS, fontsize=40, color="white", owidth=1, ocolor="black")


def draw_victory():
    """绘制胜利画面"""
    screen.clear()
    screen.fill('lightskyblue')
    screen.draw.text(f"最终得分: {game.score}", (WIDTH / 2 - 160, HEIGHT / 2 - 120), fontsize=50, color="white")
    screen.draw.text("恭喜你，通关了！", (WIDTH / 2 - 240, HEIGHT / 2 - 20), fontsize=70, color="yellow", owidth=1.5, ocolor="orange")
    restart_button.draw()


def draw_game_over():
    """绘制游戏结束画面"""
    screen.clear()
    screen.fill('lightskyblue')
    screen.draw.text("GAME OVER", (WIDTH / 2 - 260, HEIGHT / 2 - 50), fontsize=100, color="white", owidth=1.5, ocolor="black")
    restart_button.draw()


# =============================================================================
# 事件处理
# =============================================================================
def on_key_down(key):
    global pending_restart
    if key == keys.LEFT:
        input_sys.keys['left'] = True
    elif key == keys.RIGHT:
        input_sys.keys['right'] = True
    elif key == keys.UP:
        input_sys.keys['up'] = True
    elif game.state in ('victory', 'game_over') and key == keys.RETURN:
        pending_restart = True


def on_key_up(key):
    if key == keys.LEFT:
        input_sys.keys['left'] = False
    elif key == keys.RIGHT:
        input_sys.keys['right'] = False
    elif key == keys.UP:
        input_sys.keys['up'] = False


def on_mouse_down(pos, button):
    global pending_restart
    input_sys.mouse_pos = pos
    input_sys.mouse_pressed = True
    # 立即清除缓存，强制重新检测按钮
    input_sys.last_check_pos = (-1, -1)
    
    if game.state in ('victory', 'game_over'):
        mx, my = pos
        rx, ry, rw, rh = RESTART_BOUNDS
        if rx <= mx < rx + rw and ry <= my < ry + rh:
            pending_restart = True


def on_mouse_up(pos, button):
    input_sys.mouse_pressed = False
    # 清除按钮缓存
    input_sys.cached_btn = None
    input_sys.last_check_pos = (-1, -1)


def on_mouse_move(pos, buttons):
    # 优化：只在游戏运行且鼠标按下时才更新位置
    # 这样可以避免不必要的位置更新
    if input_sys.mouse_pressed and game.state == 'playing':
        input_sys.mouse_pos = pos


# =============================================================================
# 启动游戏
# =============================================================================
start_game()
pgzrun.go()
